import numpy as np
import matplotlib.pyplot as plt

def cnvs_lambda_sensitivity_corrected(M_fragments=50, lambda_factor=2, rho_c=0.01, trials=3000):
    """
    Corrected Parametric Test 3: Sensitivity to Fragmentation Factor (Lambda).
    Enforces the Coupon Collector's Problem via physical sampling without replacement.
    Strictly uses integer lambda values.
    """
    if not isinstance(lambda_factor, int) or lambda_factor < 1:
        raise ValueError(f"ERROR: lambda_factor must be an integer >= 1. Received: {lambda_factor}")

    # Creation of the physical universe of redundant data
    pool = np.repeat(np.arange(M_fragments), lambda_factor)
    N_total = len(pool)
    
    q_values = np.linspace(0.01, 0.99, 50)
    p_success = []

    for q in q_values:
        wins = 0
        k_captured = int(round(q * N_total))
        
        for _ in range(trials):
            # 1. Primary Attack: Physical capture without replacement
            captured_nodes = np.random.choice(pool, size=k_captured, replace=False)
            
            # Semantic knowledge: Unique fragments physically acquired
            unique_captured = len(np.unique(captured_nodes))
            missing_fragments = M_fragments - unique_captured
            
            # 2. Secondary Attack: Inference
            p_inference = 1.0 - (1.0 - rho_c)**unique_captured
            
            inferred_fragments = 0
            if missing_fragments > 0:
                inferred_fragments = np.random.binomial(missing_fragments, p_inference)
            
            total_known = unique_captured + inferred_fragments
            
            # 3. Victory Condition: Global Veto strict enforcement
            if total_known >= M_fragments:
                wins += 1
                
        p_success.append(wins / trials)
        
    return q_values, p_success

# ============================================================
# EXECUTION OF LAMBDA SENSITIVITY SCENARIOS
# ============================================================
plt.figure(figsize=(12, 7))
np.random.seed(20260525)

# We use ONLY strictly integer values to preserve theoretical purity
lambda_values = [1, 2, 3, 4, 5, 7, 10, 14]

# Colormap for better visualization of the progression
colors = plt.cm.viridis(np.linspace(0, 0.9, len(lambda_values)))

print("Running Corrected Test 3 (Lambda Sensitivity)...")

for lam, color in zip(lambda_values, colors):
    q_vals, p_vals = cnvs_lambda_sensitivity_corrected(
        M_fragments=50, 
        lambda_factor=lam, 
        rho_c=0.01, 
        trials=3000
    )
    plt.plot(q_vals, p_vals, color=color, linewidth=2.5, label=f'\u03BB = {lam}')

plt.axvline(x=1/3, color='black', linestyle='--', alpha=0.7, label='BFT Limit (1/3)')

plt.title("CNVS Parametric Test 3: Sensitivity to Fragmentation Factor (\u03BB)", fontsize=14, fontweight='bold')
plt.xlabel("Fraction of Physically Corrupted Nodes (q)", fontsize=12)
plt.ylabel("Attacker Win Probability (False Positive)", fontsize=12)
plt.legend(loc='upper left', fontsize=10, ncol=2)
plt.grid(True, linestyle=':', alpha=0.7)

plt.tight_layout()
plt.savefig("CNVS_Test_3_Lambda_Corrected.png", dpi=200)
plt.show()

print("Execution complete. Ready for analysis.")